Skip to content

feat: add generate_kubeconfig support to get_client#2679

Merged
myakove merged 14 commits intomainfrom
kc-file
Mar 31, 2026
Merged

feat: add generate_kubeconfig support to get_client#2679
myakove merged 14 commits intomainfrom
kc-file

Conversation

@rnetser
Copy link
Copy Markdown
Collaborator

@rnetser rnetser commented Mar 18, 2026

Summary

Add generate_kubeconfig parameter to get_client() that optionally generates a kubeconfig file for the client connection.

Changes

New module: ocp_resources/utils/client_config.py

(renamed from kubeconfig.py)

  • DynamicClientWithKubeconfig — subclass of DynamicClient with a kubeconfig attribute containing the path to the kubeconfig file
  • resolve_bearer_token() — extracts bearer token from client configuration
  • save_kubeconfig() — builds and writes kubeconfig to a temp file with 0o600 permissions, auto-cleaned via atexit

Updated: ocp_resources/resource.py

  • Added generate_kubeconfig: bool = False parameter to get_client()
  • When generate_kubeconfig=True:
    • If config_file is available: returns DynamicClientWithKubeconfig with kubeconfig pointing to the existing file (logs info, no temp file created)
    • If using host+token or config_dict: generates a temp kubeconfig file, returns DynamicClientWithKubeconfig with kubeconfig pointing to the temp file
  • When generate_kubeconfig=False (default): behavior unchanged, returns plain DynamicClient

Usage

from ocp_resources.resource import get_client

# With host+token — generates temp kubeconfig
client = get_client(host=host, token=token, verify_ssl=False, generate_kubeconfig=True)
print(client.kubeconfig)  # /tmp/tmpXXXXXX.kubeconfig

# With existing kubeconfig — points to existing file
client = get_client(config_file="~/.kube/config", generate_kubeconfig=True)
print(client.kubeconfig)  # ~/.kube/config

# Default — plain DynamicClient, no kubeconfig attribute
client = get_client()

Summary by CodeRabbit

  • New Features

    • Optional generation of kubeconfig files when creating Kubernetes clients.
    • Generated kubeconfigs use secure file permissions and respect TLS verification settings.
    • Improved bearer-token handling and precedence when establishing client credentials.
  • Tests

    • Added comprehensive tests for kubeconfig creation, contents, permissions, and error conditions.
    • Added tests covering bearer-token extraction, precedence, and edge cases.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 53f206d9-1b3d-4109-97ff-45d920fc81f9

📥 Commits

Reviewing files that changed from the base of the PR and between 79e3262 and c53ac73.

📒 Files selected for processing (2)
  • ocp_resources/utils/client_config.py
  • tests/test_resource.py
✅ Files skipped from review due to trivial changes (1)
  • tests/test_resource.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • ocp_resources/utils/client_config.py

Walkthrough

Adds kubeconfig-generation helpers and a wrapper class, and extends get_client() with a generate_kubeconfig: bool = False option to optionally produce/persist a kubeconfig and return a dynamic client that carries the kubeconfig path.

Changes

Cohort / File(s) Summary
Kubeconfig utilities
ocp_resources/utils/client_config.py
New module: DynamicClientWithKubeconfig class; resolve_bearer_token(token, client_configuration) to extract bearer tokens; save_kubeconfig(host, token, config_dict, verify_ssl) builds/writes kubeconfig YAML to a temp file with 0o600 permissions, registers atexit cleanup, and propagates I/O/serialization errors.
Client construction
ocp_resources/resource.py
get_client(..., generate_kubeconfig: bool = False) updated: builds dynamic client into a local variable, and when generate_kubeconfig is true wraps it with DynamicClientWithKubeconfig. If config_file is present uses that path; otherwise resolves token via resolve_bearer_token() and persists kubeconfig via save_kubeconfig() before wrapping.
Tests
tests/test_resource.py
Added tests for save_kubeconfig() (field correctness, permissions, verify_ssl behavior, config_dict passthrough, tempfile failure propagation) and resolve_bearer_token() (token extraction, precedence, missing/invalid cases).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding generate_kubeconfig support to the get_client function.
Description check ✅ Passed The description covers all required template sections: summary, changes breakdown, and usage examples; it is detailed and well-structured.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kc-file

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@redhat-qe-bot1
Copy link
Copy Markdown

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest python-module-install - Test Python package installation
  • /retest conventional-title - Validate commit message format
  • /retest all - Run all available tests

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. Status Checks: All required status checks must pass
  3. No Blockers: No wip, hold, has-conflicts labels and PR must be mergeable (no conflicts)
  4. Verified: PR must be marked as verified

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove
  • rnetser

Reviewers:

  • myakove
  • rnetser
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is automatically removed on each new commit
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
tests/test_resource.py (2)

312-339: Consider adding an integration test for token resolution.

These tests verify the token resolution logic in isolation by duplicating the code from get_client. While this validates the algorithm, an integration test using get_client with mocked dependencies would provide stronger coverage against regressions if the logic is refactored.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_resource.py` around lines 312 - 339, Replace this unit-style
duplicate of the token-parsing logic with an integration-style test that calls
get_client so the real resolution code is exercised: create a fake or mocked
client_configuration where client_configuration.api_key = {"authorization":
"Bearer sha256~oauth-resolved-token"}, call get_client (mock out network/cluster
calls as needed and/or monkeypatch save_kubeconfig to write to tmp_path), then
assert that save_kubeconfig was invoked with the resolved token or that the
written kubeconfig contains the expected token; reference get_client,
client_configuration.api_key, and save_kubeconfig when updating the test.

265-270: Consider adding a test for write failure handling.

The save_kubeconfig function catches OSError and logs an error without re-raising. A test verifying this behavior (e.g., by mocking os.open to raise OSError) would confirm that write failures are handled gracefully without breaking client creation.

♻️ Example test for write failure
def test_save_kubeconfig_write_failure(self, tmp_path, caplog):
    """Test that write failures are logged but don't raise."""
    kubeconfig_path = str(tmp_path / "kubeconfig")
    
    with patch("ocp_resources.utils.kubeconfig.os.open", side_effect=OSError("Permission denied")):
        save_kubeconfig(path=kubeconfig_path, host="https://api.example.com:6443", token="test-token")
    
    assert "Failed to save kubeconfig" in caplog.text
    assert not os.path.exists(kubeconfig_path)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_resource.py` around lines 265 - 270, Add a new test that verifies
save_kubeconfig handles write failures by mocking os.open to raise OSError;
specifically, create a test named test_save_kubeconfig_write_failure (in
tests/test_resource.py) that patches ocp_resources.utils.kubeconfig.os.open to
raise OSError("Permission denied"), calls save_kubeconfig(path=kubeconfig_path,
host=..., token=...), asserts the error message "Failed to save kubeconfig"
appears in caplog, and asserts the kubeconfig file does not exist after the
call.
ocp_resources/utils/kubeconfig.py (1)

20-22: Consider handling file read errors explicitly.

When config_file is provided but the file doesn't exist or is unreadable, FileNotFoundError or IOError will propagate up. This is inconsistent with the design goal of non-fatal write errors. Consider wrapping in try/except to match the error-handling pattern used for writes.

♻️ Optional: Add explicit error handling for config_file read
     elif config_file:
+        try:
             with open(config_file) as f:
                 _config = yaml.safe_load(f)
+        except OSError:
+            LOGGER.error(f"Failed to read config file {config_file}", exc_info=True)
+            return
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ocp_resources/utils/kubeconfig.py` around lines 20 - 22, Wrap the config_file
open/read in a try/except to catch FileNotFoundError/OSError/IOError and handle
it non-fatally (like the write error pattern) so a missing/unreadable config
doesn't propagate; surround the open(config_file) / yaml.safe_load(f) block and
on error log a warning (using the module's logger) including the exception
details, leave _config as None or empty, and continue execution (do not raise);
update the block referencing config_file, _config, and yaml.safe_load to
implement this behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ocp_resources/utils/kubeconfig.py`:
- Around line 20-22: Wrap the config_file open/read in a try/except to catch
FileNotFoundError/OSError/IOError and handle it non-fatally (like the write
error pattern) so a missing/unreadable config doesn't propagate; surround the
open(config_file) / yaml.safe_load(f) block and on error log a warning (using
the module's logger) including the exception details, leave _config as None or
empty, and continue execution (do not raise); update the block referencing
config_file, _config, and yaml.safe_load to implement this behavior.

In `@tests/test_resource.py`:
- Around line 312-339: Replace this unit-style duplicate of the token-parsing
logic with an integration-style test that calls get_client so the real
resolution code is exercised: create a fake or mocked client_configuration where
client_configuration.api_key = {"authorization": "Bearer
sha256~oauth-resolved-token"}, call get_client (mock out network/cluster calls
as needed and/or monkeypatch save_kubeconfig to write to tmp_path), then assert
that save_kubeconfig was invoked with the resolved token or that the written
kubeconfig contains the expected token; reference get_client,
client_configuration.api_key, and save_kubeconfig when updating the test.
- Around line 265-270: Add a new test that verifies save_kubeconfig handles
write failures by mocking os.open to raise OSError; specifically, create a test
named test_save_kubeconfig_write_failure (in tests/test_resource.py) that
patches ocp_resources.utils.kubeconfig.os.open to raise OSError("Permission
denied"), calls save_kubeconfig(path=kubeconfig_path, host=..., token=...),
asserts the error message "Failed to save kubeconfig" appears in caplog, and
asserts the kubeconfig file does not exist after the call.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5d081c13-5920-4ed7-916c-69a33de07d53

📥 Commits

Reviewing files that changed from the base of the PR and between 4f1322f and 23ff05e.

📒 Files selected for processing (3)
  • ocp_resources/resource.py
  • ocp_resources/utils/kubeconfig.py
  • tests/test_resource.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ocp_resources/utils/kubeconfig.py`:
- Around line 32-34: The code currently uses a truthy check on config_dict (if
config_dict:) which treats empty mappings like {} as "not provided" and lets
them fall through; change the conditional to explicitly test for presence (e.g.,
if config_dict is not None:) so an explicit empty mapping is accepted and
written as-is; update any related branches that assume falsy means missing to
use the same None check and ensure behavior for config_file and other branches
remains unchanged.
- Around line 35-39: Update the exception handling around YAML operations to
also catch yaml.YAMLError (and subclasses) in addition to OSError: where
yaml.safe_load(config_file) is called (the block that sets _config and logs via
LOGGER.error) and the similar block around yaml.safe_dump, catch yaml.YAMLError
so malformed or unserializable YAML is logged with exc_info=True and the
function continues as the docstring promises; reference the calls to
yaml.safe_load, yaml.safe_dump, the local variable _config, config_file, and
LOGGER.error when making the change.
- Around line 62-66: The current write (os.open(..., 0o600) + os.fdopen(...))
can leave an existing kubeconfig with world-readable perms after truncation;
instead write to a secure temporary file and atomically replace the target:
create the parent dir as before, create a temp file in the same directory (e.g.
using tempfile.NamedTemporaryFile(delete=False, dir=os.path.dirname(path)) or
build path + ".tmp"), open/create the temp with explicit 0o600 perms (os.open or
tempfile with os.fchmod), write yaml.safe_dump(_config) to the temp, fsync the
file descriptor, close it, then call os.replace(temp_path, path) to atomically
swap in the secure file; reference variables/functions: path, _config,
yaml.safe_dump, os.replace, os.open, os.fchmod (or tempfile.NamedTemporaryFile)
when making the change.

In `@tests/test_resource.py`:
- Around line 312-367: The tests currently duplicate the token-resolution branch
instead of exercising the real implementation; replace the in-test replication
with calls to the actual get_client(...) code path (passing
kubeconfig_output_path or equivalent parameter) so token resolution occurs in
production code, or refactor the resolution into a single helper (e.g.,
resolve_token_from_api_key) in the resource module and call that helper from
both get_client and the tests; update the two test functions
(test_save_kubeconfig_resolved_token_from_bearer and
test_save_kubeconfig_resolved_token_explicit_takes_precedence) to invoke
get_client(..., kubeconfig_output_path=kubeconfig_path) or the new
resolve_token_from_api_key helper instead of reimplementing the _resolved_token
logic, and keep assertions on the produced kubeconfig token.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ae40dfd-f5b3-466c-a881-dfbf6f072c9e

📥 Commits

Reviewing files that changed from the base of the PR and between 23ff05e and aa6b26a.

📒 Files selected for processing (2)
  • ocp_resources/utils/kubeconfig.py
  • tests/test_resource.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_resource.py`:
- Line 276: Calls to save_kubeconfig pass literal secret strings (e.g.,
token="test-token"), which triggers Ruff S106; replace these hardcoded token
keyword arguments with a non-literal variable or fixture (for example use a
module-level TEST_TOKEN constant, a pytest fixture like kube_token, or read from
an env var via os.environ.get("KUBE_TOKEN", "test-token")) and update every
save_kubeconfig(...) invocation in the test file (including the other
occurrences) to pass that variable (e.g., token=TEST_TOKEN or token=kube_token)
instead of the literal string.
- Around line 345-346: Test currently patches os.open but save_kubeconfig uses
tempfile.mkstemp, so change the test to patch tempfile.mkstemp to raise an
OSError (e.g. Permission denied) to exercise the write-failure path;
specifically, replace the patch target "ocp_resources.utils.kubeconfig.os.open"
with "tempfile.mkstemp" (or patch
"ocp_resources.utils.kubeconfig.tempfile.mkstemp" if importing tempfile in that
module) so save_kubeconfig hits the exception path and the test asserts the
expected handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ff283ab9-5479-4dbc-826e-bcb2e702516d

📥 Commits

Reviewing files that changed from the base of the PR and between 8b560c5 and 02a67b1.

📒 Files selected for processing (3)
  • ocp_resources/resource.py
  • ocp_resources/utils/kubeconfig.py
  • tests/test_resource.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • ocp_resources/resource.py
  • ocp_resources/utils/kubeconfig.py

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/test_resource.py (1)

245-265: Strengthen config_file test to assert byte-for-byte copy behavior.

Current assertions confirm semantic YAML equivalence, but they won’t detect content rewriting. If this flow should preserve source content as-is, assert raw bytes equality too.

✅ Test enhancement
         source_path = str(tmp_path / "source-kubeconfig")
         with open(source_path, "w") as f:
             yaml.safe_dump(source_config, f)
+        with open(source_path, "rb") as f:
+            source_raw = f.read()
@@
         with open(output_path) as f:
             saved_config = yaml.safe_load(f)
+        with open(output_path, "rb") as f:
+            output_raw = f.read()

         assert saved_config == source_config
+        assert output_raw == source_raw
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_resource.py` around lines 245 - 265, The test
test_save_kubeconfig_with_config_file currently only checks YAML semantic
equality; update it to also assert a byte-for-byte copy when save_kubeconfig is
called with config_file by reading both source_path and output_path in binary
mode and asserting their raw bytes are identical (keep or add this alongside the
existing yaml.safe_load assertion) so that save_kubeconfig's behavior of
preserving exact file content is validated; reference test name
test_save_kubeconfig_with_config_file and variables source_path/output_path and
function save_kubeconfig to locate where to add the binary-read/assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ocp_resources/utils/kubeconfig.py`:
- Around line 35-38: The code currently reads config_file into _config with
yaml.safe_load and later re-serializes it (yaml.dump), which alters
formatting/comments; instead preserve the file verbatim by copying the original
bytes: remove the yaml.safe_load/yaml.dump round-trip for the config_file branch
and use a binary copy (e.g., shutil.copyfile or open read/write in 'rb'/'wb') to
write the destination kubeconfig file; update the logic around the config_file
variable and eliminate use of yaml.safe_load/_dump for this path so the original
content is preserved exactly.
- Around line 23-24: The docstring in ocp_resources/utils/kubeconfig.py
currently states that errors are "logged but not raised," which is out-of-date;
update the docstring for the kubeconfig read/write function to state that the
function will raise exceptions on read or write failures (and that files are
created with 0o600 permissions) so the documentation matches runtime behavior;
refer to the kubeconfig module and the function that performs the kubeconfig
read/write in the docstring text when making this change.

---

Nitpick comments:
In `@tests/test_resource.py`:
- Around line 245-265: The test test_save_kubeconfig_with_config_file currently
only checks YAML semantic equality; update it to also assert a byte-for-byte
copy when save_kubeconfig is called with config_file by reading both source_path
and output_path in binary mode and asserting their raw bytes are identical (keep
or add this alongside the existing yaml.safe_load assertion) so that
save_kubeconfig's behavior of preserving exact file content is validated;
reference test name test_save_kubeconfig_with_config_file and variables
source_path/output_path and function save_kubeconfig to locate where to add the
binary-read/assert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 76566eb3-b29d-467e-9ac5-306056ad3afd

📥 Commits

Reviewing files that changed from the base of the PR and between 02a67b1 and a6fde4c.

📒 Files selected for processing (3)
  • ocp_resources/resource.py
  • ocp_resources/utils/kubeconfig.py
  • tests/test_resource.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • ocp_resources/resource.py

@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Mar 30, 2026

/verified

@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Mar 31, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@myakove myakove enabled auto-merge (squash) March 31, 2026 07:56
@myakove
Copy link
Copy Markdown
Collaborator

myakove commented Mar 31, 2026

/approve

@myakove
Copy link
Copy Markdown
Collaborator

myakove commented Mar 31, 2026

/lgtm
/approve

@rnetser
Copy link
Copy Markdown
Collaborator Author

rnetser commented Mar 31, 2026

/verified

@myakove myakove merged commit 512728d into main Mar 31, 2026
7 checks passed
@myakove myakove deleted the kc-file branch March 31, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants